/*
 * @(#)LdapMethod.java
 *
 * Copyright (c) 2003 DCIVision Ltd
 * All rights reserved.
 *
 * This software is the confidential and proprietary information of DCIVision
 * Ltd ("Confidential Information").  You shall not disclose such Confidential
 * Information and shall use it only in accordance with the terms of the license
 * agreement you entered into with DCIVision Ltd.
 */
package com.dcivision.ldap.core;

import java.io.IOException;
import java.util.Date;
import java.util.Enumeration;
import java.util.Hashtable;

import javax.naming.Context;
import javax.naming.NameAlreadyBoundException;
import javax.naming.NameNotFoundException;
import javax.naming.NamingEnumeration;
import javax.naming.NamingException;
import javax.naming.ReferralException;
import javax.naming.directory.Attribute;
import javax.naming.directory.Attributes;
import javax.naming.directory.BasicAttribute;
import javax.naming.directory.BasicAttributes;
import javax.naming.directory.DirContext;
import javax.naming.directory.InitialDirContext;
import javax.naming.directory.ModificationItem;
import javax.naming.directory.SearchControls;
import javax.naming.directory.SearchResult;
import javax.naming.ldap.Control;
import javax.naming.ldap.HasControls;
import javax.naming.ldap.InitialLdapContext;
import javax.naming.ldap.LdapContext;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

//import com.dcivision.login.Config;
import com.sun.jndi.ldap.ctl.PagedResultsControl;
import com.sun.jndi.ldap.ctl.PagedResultsResponseControl;
import com.sun.jndi.ldap.ctl.SortControl;
import com.sun.jndi.ldap.ctl.SortResponseControl;
import com.sun.jndi.ldap.ctl.VirtualListViewControl;
import com.sun.jndi.ldap.ctl.VirtualListViewResponseControl;
/**
     LdapMethod.java
     
     This class has methods of LDAP operation 
     
     @author Charlie.liu  
     @company          DCIVision Limited
     @creation date    14/12/2004
     @version          $Revision: 1.7.2.2 $
 */
public class LdapMethod {
	
	private static final Log logger = LogFactory.getLog(LdapMethod.class);
	//private Env env;
	private Hashtable paras ;

	
   /**
	* Constructor
	*/
	
	public LdapMethod(){
		
	}
	
	public NamingEnumeration search(Env env){
		paras= new Hashtable(5, 0.75f);
		paras.put(Context.INITIAL_CONTEXT_FACTORY, env.getInitctx());
		//if("NT".equals(Config.getProperty("authMethod"))){
		//	paras.put(Context.SECURITY_AUTHENTICATION,env.getAuthentication());
			paras.put(Context.SECURITY_PRINCIPAL,env.getMgr_DN());
			paras.put(Context.SECURITY_CREDENTIALS,env.getMgr_PW());			
		//}		

	  /* Specify host and port to use for directory service */
		paras.put(Context.PROVIDER_URL, env.getService());
	  try {
	    /* get a handle to an Initial DirContext */
	    DirContext ctx = new InitialDirContext(paras);

	    /* specify search constraints to search subtree */
	    SearchControls constraints = new SearchControls();
	    constraints.setSearchScope(SearchControls.SUBTREE_SCOPE);

	    /* search for all entries with surname of Jensen */                                     
	    logger.debug("Constraints:"+constraints);
	    logger.debug("Filter:"+env.getFilter());
	    logger.debug("Search Base:"+env.getSearchbase());
	                                     
	    NamingEnumeration results = ctx.search(env.getSearchbase(), env.getFilter(), constraints);	            
	    if(false){
	    
	      /* for each entry print out name + all attrs and values */
	      while (results != null && results.hasMore()) {
	        SearchResult si = (SearchResult)results.next();
        
	        /* print its name */
	        logger.debug("name: " + si.getName());
        
	        Attributes attrs = si.getAttributes();
	        if (attrs == null) {
	        	logger.debug("No attributes");
	        } else {
	        /* print each attribute */
	        for (NamingEnumeration ae = attrs.getAll(); ae.hasMoreElements();) {
	          Attribute attr = (Attribute)ae.next();
	          String attrId = attr.getID();
            
	          /* print each value */
	          for (Enumeration vals = attr.getAll(); vals.hasMoreElements();
	            logger.debug(attrId + ": " + vals.nextElement()));
	          }
	        }
	        //System.out.println();
	      }
	    }
	    paras=null;
      return results;
              
	  } catch (NamingException e) {
	  	logger.debug("LDAP search example failed."+e.getExplanation());	    	
      logger.error(e, e);
	      paras=null;
	      return null;
	  }		
  }
	
	public void compare(Env env){
	    paras.put(Context.INITIAL_CONTEXT_FACTORY, env.getInitctx());

	    /* Specify host and port to use for directory service */
	    paras.put(Context.PROVIDER_URL, env.getService());

	    DirContext ctx = null;
	    SearchControls ctls = new SearchControls();
	    ctls.setSearchScope(SearchControls.OBJECT_SCOPE);
	    ctls.setReturningAttributes(new String[0]);  // do not return any attrs

	    try {
	        /* get a handle to an Initial DirContext */
	        ctx = new InitialDirContext(paras);
	    } catch (NamingException e) {
	    	logger.debug("Cannot get initial context.");
	        return;
	    }

	    try {
	        NamingEnumeration results = 
	            ctx.search(env.getEntryDN(), "objectclass=person", ctls);

	        if (results != null && results.hasMoreElements()) {
	        	logger.debug(
	                "The value \"person\" is contained in the objectclass attribute.");
	        } else {
	        	logger.debug(
	          "The value \"person\" is not contained in the objectclass attribute." );
	        }
	    } catch (NamingException e) {
	        System.err.println("Comparison of value person failed.");
	    }

	    try {
	        NamingEnumeration results = 
	            ctx.search(env.getEntryDN(), "objectclass=xyzzy", ctls);
	        
	        if (results != null && results.hasMoreElements()) {
	        	logger.debug(
	                "The value \"xyzzy\" is contained in the objectclass attribute.");
	        } else {
	        	logger.debug(
	          "The value \"xyzzy\" is not contained in the objectclass attribute." );
	        }
	    } catch (NamingException e) {
	        System.err.println("Comparison of value xyzzy failed.");
	    }		
	}
	
	public void getAttrs(Env env){
	    paras.put(Context.INITIAL_CONTEXT_FACTORY, env.getInitctx());

	    /* Specify host and port to use for directory service */
	    paras.put(Context.PROVIDER_URL, env.getService());

	    try {
	        /* get a handle to an Initial DirContext */
	        DirContext ctx = new InitialDirContext(paras);

	        String[] attrs = new String[4];
	        attrs[ 0 ] = "cn";              /* Get canonical name(s) (full name) */
	        attrs[ 1 ] = "sn";              /* Get surname(s) (last name) */
	        attrs[ 2 ] = "mail";            /* Get email address(es) */
	        attrs[ 3 ] = "telephonenumber"; /* Get telephone number(s) */

	        Attributes result = ctx.getAttributes(env.getEntryDN(), attrs);

	        if (result == null) {
	        	logger.debug(env.getEntryDN() + 
	                               "has none of the specified attributes.");
	        } else {
	            /* print it out */
	            Attribute attr = result.get("cn");
	            if (attr != null) {
	            	logger.debug("Full name:" );
	                for (NamingEnumeration vals = attr.getAll();
	                     vals.hasMoreElements();
	                     logger.debug("\t" + vals.nextElement()))
	                    ;
	            }

	            attr = result.get("sn");
	            if (attr != null) {
	            	logger.debug("Last name (surname):" );
	                for (NamingEnumeration vals = attr.getAll();
	                     vals.hasMoreElements();
	                     logger.debug("\t" + vals.nextElement()))
	                    ;
	            }

	            attr = result.get("mail");
	            if (attr != null) {
	            	logger.debug("Email address:" );
	                for (NamingEnumeration vals = attr.getAll();
	                     vals.hasMoreElements();
	                     logger.debug("\t" + vals.nextElement()))
	                    ;
	            }
	            attr = result.get("telephonenumber");
	            if (attr != null) {
	            	logger.debug("Telephone number:" );
	                for (NamingEnumeration vals = attr.getAll();
	                     vals.hasMoreElements();
	                     logger.debug("\t" + vals.nextElement()))
	                    ;
	            }
	        }
	    } catch (NamingException e) {
	        System.err.println("Getattrs example failed.");
          logger.error(e, e);
	    }		
	}
	
	public void modAttrs(Env env){
	    paras.put(Context.INITIAL_CONTEXT_FACTORY, env.getInitctx());

	    /* Specify host and port to use for directory service */
	    paras.put(Context.PROVIDER_URL, env.getService());

	    /* specify authentication information */
	    paras.put(Context.SECURITY_AUTHENTICATION, "simple");
	    paras.put(Context.SECURITY_PRINCIPAL, env.getMgr_DN());
	    paras.put(Context.SECURITY_CREDENTIALS, env.getMgr_PW());

	    try {
	        /* get a handle to an Initial DirContext */
	        DirContext ctx = new InitialDirContext(paras);

	        /* construct the list of modifications to make */
	        ModificationItem[] mods = new ModificationItem[2];

	        Attribute mod0 = new BasicAttribute("mail", "babs@eng");
	        // Update mail attribute
	        mods[0] = new ModificationItem(DirContext.REPLACE_ATTRIBUTE, mod0);

	        // Add another value to description attribute
	        Attribute mod1 = 
	            new BasicAttribute("description", 
	                          "This entry was modified with the Modattrs program on " +
	                          (new Date()).toString());
	        mods[1] = new ModificationItem(DirContext.ADD_ATTRIBUTE, mod1);

	         /* Delete the description attribute altogether */
	        /*
	        Attribute mod1 =  new BasicAttribute("description");
	        mods[2] = new ModificationItem(DirContext.REMOVE_ATTRIBUTE, mod1);
		*/

	        /* make the change */
	        ctx.modifyAttributes(env.getEntryDN(), mods);
	        logger.debug( "modification was successful." );

	    } catch (NamingException e) {
	        System.err.println("modification failed. " + e);
	    }	
	}
	
	public void Modrdn(Env env){
	    Attribute objClasses = new BasicAttribute("objectclass");
	    objClasses.add("top");
	    objClasses.add("person");
	    objClasses.add("organizationalPerson");
	    objClasses.add("inetOrgPerson");

	    Attribute cn = new BasicAttribute("cn", "Jacques Smith");
	    Attribute sn = new BasicAttribute("sn", "Smith");
	    Attribute givenNames = new BasicAttribute("givenname", "Jacques");

	    /* Specify the DN we're adding */
	    String dn = "cn=Jacques Smith, " + env.getEntryDN();
	    /* the destination DN */
	    String ndn = "cn=Jacques M Smith, " + env.getEntryDN();
	    /* the new RDN */
	    String nrdn = "cn=Jacques M Smith";

	    
	    /*
	     * Specify the initial context implementation to use.
	     * This could also be set by using the -D option to the java program.
	     * For example,
	     *   java -Djava.naming.factory.initial=com.sun.jndi.ldap.LdapCtxFactory \
	     *       Modrdn
	     */
	    paras.put(Context.INITIAL_CONTEXT_FACTORY, env.getInitctx());

	    /* Specify host and port to use for directory service */
	    paras.put(Context.PROVIDER_URL, env.getService());

	    /* specify authentication information */
	    paras.put(Context.SECURITY_AUTHENTICATION, "simple");
	    paras.put(Context.SECURITY_PRINCIPAL, env.getMgr_DN());
	    paras.put(Context.SECURITY_CREDENTIALS, env.getMgr_PW());

	    DirContext ctx = null;

	    try {
	        /* get a handle to an Initial DirContext */
	        ctx = new InitialDirContext(paras);
	        Attributes orig = new BasicAttributes();
	        orig.put(objClasses);
	        orig.put(cn);
	        orig.put(sn);
	        orig.put(givenNames);

	        /* Add the entry */
	        ctx.createSubcontext(dn, orig);
	        logger.debug( "Added entry " + dn + ".");

	    } catch (NameAlreadyBoundException e) {
	        /* If entry exists already, fine.  Ignore this error. */
	    	logger.debug("Entry " + dn + " already exists, no need to add");
	    } catch (NamingException e) {
	        System.err.println("Modrdn: problem adding entry." + e);
	        System.exit(1);
	    }

	    try {
	        /* Delete the destination entry, for this example */
	        ctx.destroySubcontext(ndn);
	        logger.debug( "Deleted entry " + ndn + "." );

	    } catch (NameNotFoundException e) {
	        /* If entry does not exist, fine.  Ignore this error. */
	    	logger.debug( "Entry " + ndn + " is not in the directory.  " +
	                    "No need to delete.");
	    } catch (NamingException e) {
	        System.err.println("Modrdn: problem deleting entry." + e);
	        System.exit(1);
	    }


	    /* Do the modrdn operation */
	    try {
	        ctx.rename(dn, ndn);
	        logger.debug("The modrdn operation was successful.  Entry " +
	                           dn + " has been changed to " + ndn + ".");
	    } catch (NamingException e) {
	        System.err.println("Modify operation failed." + e);
	    }		
	}
	
	public void pagedSearch(String[] args,Env env){
	    paras.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");

	    /* Specify host and port to use for directory service */
	    paras.put(Context.PROVIDER_URL, env.getService());

	    try {
	        /* get a handle to an InitialLdapContext */
	        LdapContext ctx = new InitialLdapContext(paras, null);

	        /* specify search constraints to search subtree */
	        SearchControls constraints = new SearchControls();
	        constraints.setSearchScope(SearchControls.SUBTREE_SCOPE);
	        constraints.setReturningAttributes(new String[]{ "dn" });

		if (args.length != 1) {

		    System.err.println("usage: java PagedSearch <size-of-page>");
		    System.err.println("");
		    System.err.println("where <size-of-page> is the number of entries to return per page.");
		    System.exit(-1);
		}
		int pageSize = Integer.parseInt(args[0]);
		byte[] cookie = null;
		
		/*
	         * setup paged results control
	         */
	        ctx.setRequestControls(new Control[]{
			new PagedResultsControl(pageSize) });

	        /* perform the search */
		do {

	            NamingEnumeration results
			= ctx.search(env.getSearchbase(), env.getFilter(), constraints);

	            /* for each entry print out name + all attrs and values */
	            while (results != null && results.hasMore()) {
	                SearchResult si = (SearchResult)results.next();

	                /* print its name */
	                logger.debug("name: " + si.getName());

	                Attributes attrs = si.getAttributes();
	                if (attrs == null) {
	                	logger.debug("No attributes");
	                } else {
	                    /* print each attribute */
	                    for (NamingEnumeration ae = attrs.getAll();
	                         ae.hasMoreElements();) {
	                        Attribute attr = (Attribute)ae.next();
	                        String attrId = attr.getID();

	                        /* print each value */
	                        for (Enumeration vals = attr.getAll();
	                             vals.hasMoreElements();
	                             logger.debug(attrId + ": " +
					vals.nextElement()))
	                                ;
	                    }
	                }
	                System.out.println();
	            }
		    // examine the response controls
		    cookie = parseControls(ctx.getResponseControls());

	            // reset the paged results control
	            ctx.setRequestControls(new Control[]{
			new PagedResultsControl(pageSize, cookie, Control.CRITICAL) });

		} while ((cookie != null) && (cookie.length != 0));

		ctx.close();

	    } catch (NamingException e) {
	        System.err.println("PagedSearch failed.");
          logger.error(e, e);
	    } catch (java.io.IOException e) {
	        System.err.println("Sorted-View failed.");
          logger.error(e, e);
	    }	    
	}
    private static byte[] parseControls(Control[] controls) throws NamingException {

    	byte[] cookie = null;

    	if (controls != null) {

    	    for (int i = 0; i < controls.length; i++) {

    		if (controls[i] instanceof PagedResultsResponseControl) {
    		    PagedResultsResponseControl prrc =
    			(PagedResultsResponseControl)controls[i];

    		    int resultSize = prrc.getResultSize();

    		    if (resultSize != 0) {
    		    	System.out.println("***************** END-OF-PAGE " +
    			    "(result size: " + resultSize +
    			    ") *****************\n");
    		    } else {
    		    	System.out.println("***************** END-OF-PAGE " +
    			    "(result size: unknown) ***************\n");
    		    }

    		    cookie = prrc.getCookie();
    		}
    	    }
    	}

    	return (cookie == null) ? new byte[0] : cookie;
        }
    
    public void rdentry(Env env){
        paras.put(Context.INITIAL_CONTEXT_FACTORY, env.getInitctx());

        /* Specify host and port to use for directory service */
        paras.put(Context.PROVIDER_URL, env.getService());

        try {
            /* get a handle to an Initial DirContext */
            DirContext ctx = new InitialDirContext(paras);

            /* Read Babs' entry */
            Attributes attrs = ctx.getAttributes(env.getEntryDN());

            if (attrs == null) {
            	logger.debug(env.getEntryDN() + "has no attributes");
            } else {
                /* print each attribute */
                for (NamingEnumeration ae = attrs.getAll();
                     ae.hasMoreElements();) {
                    Attribute attr = (Attribute)ae.next();
                    String attrId = attr.getID();

                    /* print each value */
                    for (NamingEnumeration vals = attr.getAll();
                         vals.hasMoreElements();
                         logger.debug(attrId + ": " + vals.nextElement()))
                        ;
                }
            }
        } catch (NamingException e) {
            System.err.println("Rdentry example failed.");
            logger.error(e, e);
        }    	
    }
    
    public void refSearch(Env env){
        paras.put(Context.INITIAL_CONTEXT_FACTORY, env.getInitctx());

        /* Specify host and port to use for directory service */
        paras.put(Context.PROVIDER_URL, env.getService());

        /* Throw exception when referral is encountered */
        paras.put(Context.REFERRAL, "throw");

        try {
            /* get a handle to an Initial DirContext */
            DirContext ctx = new InitialDirContext(paras);

            /* specify search controls to search subtree */
            SearchControls controls = new SearchControls();
            controls.setSearchScope(SearchControls.SUBTREE_SCOPE);

            /* search for all entries with surname of Jensen */
            NamingEnumeration results;

            while (true) {
                try {
                    results =
                        ctx.search(env.getSearchbase(), env.getFilter(), controls);

                    /* for each entry print out its details */
                    while (results != null && results.hasMore()) {
                    	logger.debug((SearchResult) results.next());
                    }
                    break;

                } catch (ReferralException e) {
                	try{
                    
                    if (! followReferral(e.getReferralInfo())) {
                        if (! e.skipReferral()) {
                            return;
                        }
                    }
                	}catch(Exception exc){
                		
                	}
                    /* point to the new context */
                    ctx = (DirContext) e.getReferralContext();
                }
            }
        } catch (NamingException e) {
            System.err.println("RefSearch example failed.");
            logger.error(e, e);
        }    	
    }
    private static boolean followReferral(Object referralInfo) throws IOException {
        byte[] reply = new byte[4];

        System.out.println("Received referral: " + referralInfo);
        System.out.print("Follow it? [y/n]");
        System.in.read(reply);

        return (reply[0] != 'n');
    }
    
    public void retryRefSearch(Env env){
    	paras.put(Context.INITIAL_CONTEXT_FACTORY, env.getInitctx());

        /* Specify host and port to use for directory service */
        paras.put(Context.PROVIDER_URL, env.getService());

        /* Throw exception when referral is encountered */
        paras.put(Context.REFERRAL, "throw");

        try {
            /* get a handle to an Initial DirContext */
            DirContext ctx = new InitialDirContext(paras);

            /* specify search controls to search subtree */
            SearchControls controls = new SearchControls();
            controls.setSearchScope(SearchControls.SUBTREE_SCOPE);

            /* search for all entries with surname of Jensen */
            NamingEnumeration results;

            for (boolean moreReferrals = true; moreReferrals;) {
                try {
                    results =
                        ctx.search(env.getSearchbase(), env.getFilter(), controls);

                    /* for each entry print out its details */
                    while (results != null && results.hasMore()) {
                    	logger.debug((SearchResult) results.next());
                    }

    		moreReferrals = false;

                } catch (ReferralException e) {
                	try{
                    if (! followReferral(e.getReferralInfo())) {
    		    moreReferrals = e.skipReferral();
                    }
                	}catch(Exception exc){
                    	
                    }

    		while (moreReferrals) {

    		    try {
                            /* attempt to connect to the new context */
                            ctx = (DirContext) e.getReferralContext(paras);
    			break;

    		    } catch (NamingException ne) {
    		    	try{
    		    		if (retryReferral(ne)) {
    		    			// modify environment properties (env), if necessary
    			            e.retryReferral();
    			            } else {
    			            	moreReferrals = e.skipReferral();
    			            }
    		    	}catch(Exception exc){
    		    		
    			}
    		    }
    		}
                }
            }
        } catch (NamingException e) {
            System.err.println("RetryRefSearch example failed.");
            logger.error(e, e);
        }    	
    }

    private static boolean retryReferral(Exception e) throws IOException {
        byte[] reply = new byte[4];

        System.out.println("Error encountered: " + e);
        System.out.print("Retry? [y/n]");
        System.in.read(reply);

        return (reply[0] != 'n');
    }
    
    public  void sortedList(String[] args,Env env){
    	paras.put(Context.INITIAL_CONTEXT_FACTORY, env.getInitctx());

    	/* Specify host and port to use for directory service */
    	paras.put(Context.PROVIDER_URL, env.getService());

    	try {
    	    // Get a handle to an LdapContext for setting controls
    	    LdapContext ctx = new InitialLdapContext(paras, null);

    	    if (args.length != 1) {
    		System.err.println("usage: java SortedList key");
    		System.exit(-1);
    	    }

    	    String attrID = args[0];

    	    // Create "critical" sort control using attrID supplied
    	    Control sortCtl = 
    		new SortControl(new String[]{attrID}, Control.CRITICAL);

    	    // Set request control to contain SortControl
    	    ctx.setRequestControls(new Control[]{sortCtl});

    	    // Do list as before
    	    printNameList(env.getSearchbase(), 
    		ctx.list(env.getSearchbase()));
    	} catch (Exception e) {
        logger.error(e, e);
    	}    	
    }
    private static void printNameList(String msg, NamingEnumeration nl) {
    	System.out.println(msg);
    	if (nl == null) 
    	    System.out.println("No items in name list");
    	else {
    	    try {
    		while (nl.hasMore())
    		    System.out.println(nl.next());
    	    } catch (NamingException e) {
            logger.error(e, e);
    	    }
    	}
    }
    public void sortedView(String[] args,Env env){
        paras.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");

        /* Specify host and port to use for directory service */
        paras.put(Context.PROVIDER_URL, env.getService());

        try {
            /* get a handle to an InitialLdapContext */
            LdapContext ctx = new InitialLdapContext(paras, null);

            /* specify search constraints to search subtree */
            SearchControls constraints = new SearchControls();
            constraints.setSearchScope(SearchControls.SUBTREE_SCOPE);
            constraints.setReturningAttributes(new String[]{ "dn" });

    	if (args.length != 3) {

    	    System.err.println("usage: java SortedView <sort-by> <percentage-mark> <size-of-view>");
    	    System.err.println("");
    	    System.err.println("where <sort-by> is the attribute ID to sort by,");
    	    System.err.println("  and <percentage-mark> is a number in the range 0 to 100,");
    	    System.err.println("  and <size-of-view> is the number of entries to return.");
    	    System.err.println("");
    	    System.err.println("example: to view 20 entries at the 50% mark sorted by the 'uid' attribute:");
    	    System.err.println("   java SortedView uid 50 20");
    	    System.exit(-1);
    	}
    	
    	/*
             * Sort and retrieve entries at a specific position in the list.
             */
            ctx.setRequestControls(new Control[]{
    		new SortControl(new String[]{ args[0] }, Control.CRITICAL),
    		new VirtualListViewControl(Integer.parseInt(args[1]),
    					   Integer.parseInt(args[2]),
    					   Control.CRITICAL),
    	});

            /* perform the search */
            NamingEnumeration results
                = ctx.search(env.getSearchbase(), "("+args[0]+"=*)", constraints);

            /* for each entry print out name + all attrs and values */
            while (results != null && results.hasMore()) {
                SearchResult si = (SearchResult)results.next();

                /* print its name */
                logger.debug("name: " + si.getName());

                Attributes attrs = si.getAttributes();
                if (attrs == null) {
                	logger.debug("No attributes");
                } else {
                    /* print each attribute */
                    for (NamingEnumeration ae = attrs.getAll();
                         ae.hasMoreElements();) {
                        Attribute attr = (Attribute)ae.next();
                        String attrId = attr.getID();

                        /* print each value */
                        for (Enumeration vals = attr.getAll();
                             vals.hasMoreElements();
                             logger.debug(attrId + ": " + vals.nextElement()))
                                ;
                    }
                }
    	    // examine the response controls (if any)
    	    if (si instanceof HasControls) {
    		parseControl(((HasControls)si).getControls());
    	    }
                System.out.println();
            }
    	// examine the response controls (if any)
    	parseControl(ctx.getResponseControls());

    	ctx.close();

        } catch (NamingException e) {
            System.err.println("Sorted-View failed.");
            logger.error(e, e);
        } catch (java.io.IOException e) {
            System.err.println("Sorted-View failed.");
            logger.error(e, e);
        }    	
    }
    private static void parseControl(Control[] controls) throws NamingException {

    	if (controls != null) {

    	    for (int i = 0; i < controls.length; i++) {

    		if (controls[i] instanceof SortResponseControl) {
    		    SortResponseControl src = (SortResponseControl)controls[i];
    		    if (src.isSorted()) {
    			System.out.println("Sorted-Search completed successfully");
    		    } else {
    			System.out.println("Sorted-Search did not complete successfully: error (" + src.getResultCode() + ") on attribute '" + src.getAttributeID() + "'");
    			throw src.getException();
    		    }
    		} else 
    		    if (controls[i] instanceof VirtualListViewResponseControl) {

    		    int rc;
    		    VirtualListViewResponseControl vlvrc =
    			(VirtualListViewResponseControl)controls[i];

    		    if ((rc = vlvrc.getResultCode()) == 0) {
    			System.out.println("Sorted-View completed successfully");
    		    } else {
    			System.out.println("Sorted-View did not complete successfully: error " + rc);
    		    }

    		} else {
    		    Control c = controls[i];
    			if (c.isCritical()) {
    			    System.out.println("Received critical control: "+ c.getID());
    			} else {
    			    System.out.println("Received non-critical control: "+ c.getID());
    			}
    		}
    	    }
    	}
    }     
}
